<?php
/**
this code forms the backbone of a little web service thingy that simply 
receives d20 style die roll requests, and responds with an XML 
doc containing the dice roll results.

I have enhanced the d20 notation by adding an optional multiplier at the end 
to indicate that the roll should be performed multiple times.
For example...
1d20 // requests a single 20 sided dice be rolled once
2d20 // requests two 20 sided dice get roll, and the values summed
2d20+1 // requests two 20 sided dice get rolled, and the values summed, then 1 get added to the total.
2d20+1*2  // requests two 20 sided dice get rolled, and the values summed, then 1 get added to the total, then the entire process repeated again (a total of 2 times)

d20 notation is *not* case sensitive. 1d20 == 1D20

q (query) can contain comma separated list of queries? eg: 1d20,1d8
This would allow multiple die types to be batched. 
A common gaming use would be 1d20 (to hit), followed by 1d8 (for damage)
*/
/*
Copyright 2008 Lucien Stals 

Licensed under the Apache License, Version 2.0 (the "License"); 
you may not use this file except in compliance with the License. You may obtain a copy of the License at 

http://www.apache.org/licenses/LICENSE-2.0 

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 
*/
/*
Source code available http://code.google.com/p/xml-dice-roller/
*/

// The regex I'm validating roll requests with...
$expression = '/^(\d+)[dD](\d+)([\+-]{1}\d+)?(\*(\d+))?$/';

// flag to see if we gennerated any output on this call
$hasOutput = false;
$output = "";

if(isset($_GET['q'])) 
{
	// we have a non empty query, generate results
	// break query on commas...
	$bits = explode(',',$_GET['q']);
	
	foreach($bits as $bit) // outermost loop for coma seperated batch rolls.
	{
		// first check we can match the query. If not, return ... ?
		// split q into it's bits...
		$results = preg_match($expression, trim($bit), $matches);
		if($results !== false && $results > 0)
		{	
			// this roll passed validation. we will be able to generate output.
			// note: for 1d20+2*3 we get the following array
			// [1][20][+2][*][3]
			// but for 1d20*2 we get
			// [1][20][][*][3]
			// where the multiplier is in the index the modifier is normaly expected.
			
			// how many times to roll? the "1" in "1d20"
			$times = $matches[1];
			
			// die type? the "20" in "1d20"
			// "die" is reserved word in php. best avoid it.
			//$die = $matches[2];
			$dice = $matches[2];
			
			// $matches[3] might be null.
			// modifier? the "+2" in "1d20+2"
			$modifier = 0;
			if(isset($matches[3]) && $matches[3] != "") // this catches case of null modifier.
			{
				$modifier = $matches[3];
			}
			
			// multiplier? the "*3" in "1d20+2*3"
			$rolls = 1;
			if(isset($matches[5]))
			{
				$rolls = $matches[5];
			}
			if($rolls > 100) $rolls = 100; // cap repeat rolls at 100. would anyone *need* more?
			
			$hasOutput = true;
			
			for($i = 0; $i < $rolls; $i++) // loop once per batch roll (the *n part).
			{
				$output .= "\t<roll>\n";
				$want = $times . "d" . $dice;
				if($modifier != 0) $want .= $modifier;
				$output .= "\t\t<want>$want</want>\n";
				$output .= "\t\t<raw>";
				$raw = 0;
				for($j = 0; $j < $times; $j++) // roll them bones! Innermost loop
				{
					$raw += rand(1, $dice);
				}
				$total = $modifier + $raw;
				$output .= "$raw</raw>\n";
				$output .= "\t\t<modifier>$modifier</modifier>\n";
				$output .= "\t\t<total>$total</total>\n";
				$output .= "\t</roll>\n";
			}
		}
		else
		{
			// the next batched query might still be good.
			continue;
			// TODO: this hasn't been tested yet.
		}
	}
	
	// if no output, generate error...
	if($output !== "")
	{
		// we got this far. must have some output...
		header('Content-type: text/xml');
		echo "<xml version=\"1.0\">\n";
		echo "<results>\n";
		echo $output;
		echo "</results>\n";
		echo "</xml>\n";
	}
	else
	{
		header("HTTP/1.0 400 Bad Request"); // is this appropriate? sufficient?
		// TODO: err msg not happening. check this
	}
}
?>
